/**
 * 主窗口程序逻辑
 * @type {Electron}
 */
const { remote, clipboard } = require('electron');
const { app } = remote;
const projectRoot = remote.getGlobal('projectPath');
const srcPath = remote.getGlobal('srcPath');
// const ipc = require(`${srcPath}/mainlib/ipc`);
const path = require('path');
const { Menu, MenuItem, BrowserWindow, Notification } = remote;
const { animation_support } = require(`${srcPath}/mainlib/window-animation`);
const core = require(`${srcPath}/mainlib/core`);
const Type = require(`${srcPath}/mainlib/type`);
const fs = require('fs');
const fswin = require('fswin');
const wh = require(`${srcPath}/mainlib/windows-helper`);
const childProcess = require('child_process');
const ofs = require('original-fs');
const regeditTools = require('regedit');
const FileType = require(`${srcPath}/file-type`);
/** @var WndMgr WindowManager */
const WndMgr = require(`${srcPath}/windows`);

const ALIAS_TYPE = {
    LINK: 'link',
    FILE: 'file',
    NAME: 'name',
    EXT: 'ext',
};

let SystemRootPath = process.env.SystemRoot;
let AllUserProfilePath = process.env.ALLUSERSPROFILE;
let AppDataPath = process.env.APPDATA;
let LocalAppDataPath = process.env.LOCALAPPDATA;

let RegRightMenuCache = {};


if (core.isInAsar()) {
    regeditTools.setExternalVBSLocation(path.dirname(projectRoot) + '/node_modules/regedit/vbs');
}

WndMgr.listen();

var t = new Vue({
    el: '#root',

    data: {

        //界面参数
        ui: {
            //搜索匹配项ui调节
            itemWidth: 100,
            itemMargin: 10,
        },

        // 默认文件类型优先级, 从左到右优先级从高到低
        extnamePriorities: ['.exe', '.com', '.cmd', '.bat', '.vbs', 'dir'],

        buildInFiles: {

        },

        forceCursor: null,


        //主窗口是否显示, 用于做窗口动画
        windowShow: false,

        //右上角菜单
        menu: null,

        //匹配项目的右键菜单
        itemRightMenu: null,

        //主窗口
        mainWindow: null,

        //搜索历史
        historyData: core.getHistory(),

        //搜索关键字
        keyword: '',
        //正在搜索的关键字(异步搜索)
        searchingKeyword: '',

        //正在搜索文件
        isSearchingFiles: false,
        //正在搜索索引
        isSearchingIndexes: false,
        //搜索loading文本
        loadingText: '正在搜索..',

        //后台配置
        settings: {},

        //搜索索引
        searchIndexes: {},
        pinyinEngine: null,

        //监控目录
        directories: {},
        //已经搜索过的目录
        scannedDirectories: {},
        //是否正在搜索目录
        scanningDirectory: false,

        //正在监听的目录监视器
        watchingDirectories: {},

        searchTimer: null,

        //搜索结果(匹配项)
        list: [],
        //搜索结果选中的下标
        activedIndex: 0,

        //图标缓存
        indexIcons: {},

        //停止目录文件搜索
        stopScan: false,

        ready: false,

        status: {
            search_ended: false,
            continue_to_search: false
        },

        lastSearchIndex: null,

        searchingThread: null,
        searchResultListerning: false,

        config: {
            "search.delay_after_keyword_change": 200
        }
    },

    /**
     * 程序初始化
     */
    mounted() {
        this.mainWindow = remote.getGlobal('mainWindow');
        this.windowShow = this.mainWindow.isVisible();

        /**
         * 监听主窗口变化(显示/隐藏)
         */
        core.on(core.ET.TOGGLE_MAIN_WINDOW_VISIBLE, (e, a) => {
            this.windowShow = a.show;

            if (a.show) {
                this.$refs.searchInput.select();
            }
        });

        /**
         * 监听后台配置修改
         */
        core.on(core.ET.SETTING_CHANGE, () => {
            let oldSettings = this.settings;
            let newSettings = core.getSetting();

            if (JSON.stringify(oldSettings.directories) != JSON.stringify(newSettings.directories)) {
                this.settings = newSettings;

                if (this.isSearchingFiles) {
                    this.stopScan = true;
                }

                this.initDirectories(oldSettings);
            }
        });

        /**
         * 监听键盘按键
         */
        window.addEventListener('keydown', e => {
            this.handleKeyboard(e);
        });

        /**
         * 初始化文件后缀优先级
         * @type {{}}
         */
        let extPriority = {};

        for (let i = 0; i < this.extnamePriorities.length; i++) {
            let row = this.extnamePriorities[i];
            extPriority[row] = this.createPriorityExtname(this.extnamePriorities.length - i);
        }

        if (!this.searchResultListerning) {
            core.on(core.ET.CM_SEARCH_RESULT, (e, data) => {
                this.recvSearchResult(data);
            });

            this.searchResultListerning = true;
        }

        this.extnamePriorities = extPriority;
        this.loadSettings();
        this.createMenu();

        let oldLoadingText = this.loadingText;
        this.loadingText = '正在初始化数据..';

        new Notification({title: 'Quick Start 正在加载数据, 请稍后...'}).show();

        this.updateAllFileAlias(() => {
            this.initDirectories();
            this.initFileType();
            this.addBuildInFilesToIndexes();

            setInterval(() => this.updateAllFileAlias(), 3600 * 1000);

            this.$nextTick(() => {
                this.$refs.searchInput.select();
            });

            this.scanAllDirectories(null, () => {
                this.ready = true;
                this.loadingText = oldLoadingText;
                new Notification({title: 'Quick Start 已经准备就绪'}).show();
            });
        });
        //
        // this.initSearchingThread().then((searchWnd) => {
        //     core.trigger(core.ET.CM_SEARCH, {keyword:123});
        // });
    },

    computed: {
        isLoading() {
            return this.isSearchingFiles || this.isSearchingIndexes || !this.ready;
        }
    },

    watch: {
        /**
         * 关键字更改后执行搜索
         */
        keyword() {
            let kw = this.keyword.trim();
            clearTimeout(this.searchTimer);

            if (kw != '' && kw != '.') {
                let keyword = this.keyword.trim();
                this.searchingKeyword = keyword;
                this.activedIndex = 0;

                this.searchTimer = setTimeout(() => {
                    this.list = [];
                    this.search(keyword, false);
                }, this.config['search.delay_after_keyword_change']);
            } else {
                this.list = [];
                this.searchingKeyword = '';
            }
        }
    },

    methods: {
        /**
         * 根元素的class
         * @returns {{"wnd-show": boolean}}
         */
        rootClass() {
            return {
                'wnd-show': this.windowShow
            }
        },

        rootStyles() {
            let style = {};

            if (this.forceCursor) {
                style.cursor = `${this.forceCursor} !important`;
            }

            return style;
        },


        /**
         * 处理键盘按键
         * @param e
         */
        handleKeyboard(e) {
            let moveIndex = (offsetX, offsetY) => {
                let index = this.activedIndex;
                let itemList = this.$refs.itemList;
                let totalWidth = itemList.offsetWidth;
                let itemWidth = this.ui.itemWidth;

                let style = getComputedStyle(itemList, null);
                let pl = parseFloat(style.paddingLeft),
                    pr = parseFloat(style.paddingRight);
                let ml = parseFloat(style.marginLeft),
                    mr = parseFloat(style.marginRight);
                totalWidth -= pl + pr + ml + mr;

                let totalXLen = parseInt(totalWidth / (itemWidth + this.ui.itemMargin));
                let totalX = totalXLen - 1;
                let totalYLen = Math.ceil(this.list.length / totalXLen);
                let totalY = totalYLen - 1;

                let currentX = index % totalXLen;
                let currentY = Math.floor(index / totalXLen);

                currentX += offsetX;
                currentY += offsetY;

                if (currentX >= totalXLen) {
                    currentY++;
                    currentX = 0;
                }

                if (currentX < 0 && currentY > 0) {
                    currentY--;
                    currentX = totalX;
                }

                currentX = Math.min(totalX, Math.max(0, currentX));
                currentY = Math.min(totalY, Math.max(0, currentY));

                this.activedIndex = Math.min(this.list.length - 1, Math.max(0, currentX + currentY * totalXLen));
            };

            if (this.list) {
                if (e.code == "Enter") {
                    if (this.list[this.activedIndex]) {
                        this.shellOpenItem(this.list[this.activedIndex]);
                    }

                    e.preventDefault();
                } else if (e.code == "Escape") {
                    this.keyword = '';
                    this.mainWindow.animateHide();
                } else if (e.code == "Tab") {
                    if (e.shiftKey) {
                        this.activedIndex--;
                    } else {
                        this.activedIndex++;
                    }

                    if (this.activedIndex >= this.list.length) {
                        this.activedIndex = this.list.length - 1;
                    }

                    if (this.activedIndex < 0) {
                        this.activedIndex = 0;
                    }

                    e.preventDefault();
                } else if (e.code == 'ArrowUp') {
                    moveIndex(0, -1);
                } else if (e.code == 'ArrowDown') {
                    moveIndex(0, 1);
                } else if (e.code == 'ArrowLeft') {
                    moveIndex(-1, 0);
                } else if (e.code == 'ArrowRight') {
                    moveIndex(1, 0);
                }
            }
        },

        recvSearchResult(data) {
            console.log('search result', data);
        },

        /**
         * 继续搜索
         */
        continueToSearch() {
            if (this.keyword) {
                this.search(this.keyword, true);
            }
        },

        /**
         * 初始化搜索线程
         */
        initSearchingThread() {
            return new Promise((ok, fail) => {
                if (this.searchingThread) {
                    ok(this.searchingThread);
                } else {
                    let wnd = this.searchingThread = new BrowserWindow({
                        width: 1000,
                        height: 700,
                        show: false,
                        frame: true,
                        resizable: true,
                        minimizable: true,

                        title: 'searchingThread',
                        webPreferences: {
                            nodeIntegration: true,
                        },
                    });

                    wnd.on('close', () => {
                        this.searchingThread = null;
                    });

                    wnd.once('ready-to-show', () => {
                        console.log('show');
                        wnd.show();
                        wnd.webContents.openDevTools();
                        ok(this.searchingThread);
                    });

                    wnd.loadFile(path.join(__dirname, 'searching-thread.html'));
                }
            });
        },

        /**
         * 从注册表查找所有exe文件的别称
         * <br>例如: regedit.exe => 注册表编辑器
         * @param {function} callback 
         */
        updateAllFileAlias(callback) {
            let key = "HKCU\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache";
            let timer = setTimeout(function() {
                callback && callback();
            }, 5000);

            regeditTools.list(key, (e, data) => {
                clearTimeout(timer);

                if (e) {
                    callback && callback();
                    return;
                }

                let values = data[key].values;

                for (let i in values) {
                    if (/^[a-z]:/i.test(i) && /\.FriendlyAppName$/i.test(i)) {
                        FileType.registerBuildInFiles(i.replace(/\.FriendlyAppName$/i, ''), {
                            alias: values[i].value
                        });
                    }
                }

                callback && callback();
            });
        },

        initFileType() {
            FileType.registerBuildInFiles(require(`${srcPath}/build-in-files`));
        },

        /**
         * 搜索结果item项的动态class
         * @returns {{marginRight: string, width: string, marginBottom: string}}
         */
        itemListStyles() {
            return {
                width: this.ui.itemWidth + 'px',
                marginRight: this.ui.itemMargin + 'px',
                marginBottom: this.ui.itemMargin + 'px',
            };
        },

        /**
         * 获取搜索结果的tag (例如:桌面, 系统等)
         * @param path
         * @returns {null|{label: string, class: string}}
         */
        getTag(path) {
            if (this.isInDesktop(path)) {
                return { class: 'desktop', label: '桌面' };
            } else if (this.isInDirectory(path, process.env.SystemRoot)) {
                return { class: 'system', label: '系统' };
            } else if (this.isInStartMenu(path)) {
                return { class: 'start-menu', label: '开始菜单' };
            } else if (this.inCustomDirectiry(path)) {
                return { class: 'custom', label: '自定义目录' };
            }

            return null;
        },

        /**
         * 获取指定后缀文件的后缀优先级
         * @param ext
         * @returns {string|*|number}
         */
        getExtnamePriority(ext) {
            if (this.extnamePriorities[ext]) {
                return this.extnamePriorities[ext];
            }

            return 0;
        },

        /**
         * 加载后台配置
         */
        loadSettings() {
            this.settings = core.getSetting();
        },

        /**
         * 显示右上角菜单
         */
        showMenu() {
            this.menu.popup({ window: remote.getCurrentWindow() })
        },

        /**
         * 用于扫描目录
         * @param dirPath
         * @param depth
         * @param callback
         * @param finish
         */
        scanDirectory(dirPath, depth, callback, finish) {
            if (!fs.existsSync(dirPath)) {
                finish && finish();
                return;
            }

            let stack = 0;

            depth = Type.isUndefined(depth) ? -1 : depth;

            let _scan = (dirPath, currentDepth, callback) => {
                if (depth >= 0 && currentDepth > depth) {
                    return;
                }

                stack++;

                fs.readdir(dirPath, (e, files) => {
                    stack--;

                    if (e) {
                        console.log(e);
                        if (stack == 0) {
                            finish && finish();
                        }

                        return;
                    }

                    files.forEach(a => {
                        let file = path.join(dirPath, a);
                        callback(file);

                        try {
                            if (fs.statSync(file).isDirectory()) {
                                _scan(file, currentDepth + 1, callback);
                            }
                        } catch (e) {
                            console.error(e);
                        }
                    });

                    if (stack == 0) {
                        finish && finish();
                    }
                })
            };

            _scan(dirPath, 0, callback);
        },

        createPriorityKeywordPos(priority) {
            return priority * 100000000;
        },

        createPriorityMatch(priority) {
            return parseFloat(Math.max(0, Math.min(1, parseFloat(priority))).toFixed(2)) * 10000000;
        },

        createPriorityLocation(priority) {
            return priority * 1000;
        },

        createPriorityExtname(priority) {
            return priority;
        },

        /**
         * 搜索
         * @param keyword
         * @param itemCount
         * @param continueToSearch
         */
        search(keyword, continueToSearch, itemCount) {
            let runningFile = {};
            let runningProcesses = null;
            this.searchingKeyword = keyword;
            this.addSearchHistory(keyword);

            if(!continueToSearch) {
                this.list = [];
                this.lastSearchIndex = null;
            }

            itemCount = itemCount ? itemCount :
                Math.floor((this.$refs.itemList.offsetWidth - 20) / 110) *
                Math.floor((this.$refs.itemList.offsetHeight - 20 - 30) / 122)
            ;

            // itemCount = 5;

            // 查找匹配项 
            let _match = itemIndex => {
                let item = Type.clone(itemIndex);
                item.nameLabel = item.name;
                item.descriptionLabel = item.description;
                item.priority = item.basePriority;
                let pinyinEngine = this.getPinyinEngine();

                let after_match = (item) => {
                    if (item.state && item.state.isDirectory()) {
                        item.priority += this.getExtnamePriority('dir');
                    } else {
                        item.priority += this.getExtnamePriority(item.extname);
                    }

                    if (item.extname == '.exe' || item.targetExtname == '.exe') {
                        if (!runningProcesses) {
                            runningProcesses = [
                                ...new Set(wh.wmicGetAllProcess()
                                    .map(a => core.correctPath(a.ExecutablePath.trim().toLowerCase()))
                                    .filter(a => !!a))
                            ];
                        }

                        let path = item.target ? item.target : item.path;

                        if (runningFile[path]) {
                            item.running = true;
                        } else {
                            runningFile[path] = -1 != runningProcesses.indexOf(core.correctPath(path.toLowerCase()));
                            item.running = runningFile[path];
                        }
                    }

                    return item;
                };

                let matchResult = pinyinEngine.matchIndex(this.searchingKeyword, item.nameIndex, false, true);

                if (matchResult) {
                    item.nameLabel = pinyinEngine.matchIndexWithTag(this.searchingKeyword, item.nameIndex, true, true);
                    item.priority += this.createPriorityKeywordPos(9);
                    item.priority += this.createPriorityMatch(matchResult.length / item.name.length);
                    return after_match(item);
                }

                if (item.secondNameIndex) {
                    matchResult = pinyinEngine.matchIndex(this.searchingKeyword, item.secondNameIndex, false, true);

                    if (matchResult) {
                        // item.secnameLabel = pinyinEngine.matchIndexWithTag(this.searchingKeyword, item.secondNameIndex, true, true);
                        item.priority += this.createPriorityKeywordPos(9);
                        item.priority += this.createPriorityMatch(matchResult.length / item.secondName.length);
                        return after_match(item);
                    }
                }

                matchResult = pinyinEngine.matchIndex(this.searchingKeyword, item.descriptionIndex, false, true);

                if (matchResult) {
                    // item.descriptionLabel = pinyinEngine.matchIndexWithTag(this.searchingKeyword, item.descriptionLabel, true, true);
                    item.priority += this.createPriorityKeywordPos(7);
                    item.priority += this.createPriorityMatch(matchResult.length / item.description.length);
                    return after_match(item);
                }

                return null;
            };

            /**
             * 搜索完成回调
             * @param {Array[{*}]} matcheds 
             * @param {boolean} ended 
             */
            let _searchFinish = (ended) => {
                this.status.search_ended = ended;
                this.isSearchingIndexes = false;
                
            };
            // this.scanAllDirectories(_match);

            this.isSearchingIndexes = true;
            

            //开始搜索
            setTimeout(() => {
                let continuePositionFound = false;
                let currentMatchedCount = 0;
                let lastIndex = null;
                let ended = true;

                for (let filePath in this.searchIndexes) {
                    //继续到上次搜索的地方开始
                    if (continueToSearch && !continuePositionFound) {
                        if (filePath == this.lastSearchIndex) {
                            continuePositionFound = true;
                        }

                        continue;
                    }

                    // 查找匹配项
                    let item = _match(this.searchIndexes[filePath]);
                    lastIndex = this.lastSearchIndex;
                    this.lastSearchIndex = filePath;

                    if (item) {
                        // 多搜索一个,用于确定是否还能继续搜索,用于解决以下问题:
                        // 当搜索处于末尾,并且搜索匹配的数量刚好等于指定值时
                        // 无法知道是否可以继续下一次搜素, 下次继续搜索也肯能
                        // 是空的, 但是按钮依旧会显示.
                        if (currentMatchedCount < itemCount) {
                            currentMatchedCount ++;
                            this.appendToListByPriority(item);
                        } else {
                            this.lastSearchIndex = lastIndex;
                            ended = false;
                            break;
                        }
                    }
                }

                _searchFinish(ended);
            }, 20);
        },

        /**
         * 单次搜索完成
         * @param {array[string]} files 
         */
        onSearchFinish(files) {
            // this.appendToListByPriority(item);
        },

        /**
         * 搜索到底
         */
        onSearchEnd() {

        },

        /**
         * 扫描所有目录
         * @param {type} matchFunc
         * @param {type} callback
         * @returns {undefined}
         */
        scanAllDirectories(matchFunc, callback) {
            if (!this.scanningDirectory) {
                this.scanningDirectory = true;
                let dirs = Object.values(Type.clone(this.directories));
                this.isSearchingFiles = true;

                let _scan = () => {
                    if (dirs.length == 0 || this.stopScan) {
                        this.isSearchingFiles = false;
                        this.scanningDirectory = false;
                        this.stopScan = false;
                        callback && callback();
                        return;
                    }

                    let item = dirs.shift();
                    let path = item.path;

                    if (this.scannedDirectories[path]) {
                        _scan();
                        return;
                    }

                    this.scanDirectory(path, this.directories[path].watchingOption.depth, file => {
                        let itemIndexes = this.getDirectoryIndex(file);

                        if (this.stopScan) {
                            this.isSearchingFiles = false;
                            this.scanningDirectory = false;
                            this.stopScan = false;
                            return;
                        }

                        if (itemIndexes) {
                            return;
                        } else {
                            itemIndexes = this.updateIndex(file, this.directories[path]);
                        }

                        if (this.searchingKeyword == '') {
                            return;
                        }

                        if (matchFunc) {
                            let matched = null;

                            if (itemIndexes) {
                                matched = _match(itemIndexes);
                            }

                            if (matched) {
                                this.appendToListByPriority(matched);
                            }
                        }
                    }, () => {
                        this.scannedDirectories[path] = true;
                        _scan();
                    });
                };

                _scan();
            }
        },

        /**
         * 添加搜索历史记录
         * @param keyword
         */
        addSearchHistory(keyword) {
            core.addHistory(keyword);
            this.historyData = core.getHistory();
        },

        /**
         * 将item添加到结果列表(按照优先级)
         * @param item
         */
        appendToListByPriority(item) {
            let path = core.correctPath(item.path);
            FileType.updateIcon(item);

            for (let i = 0; i < this.list.length; i++) {
                let listPath = core.correctPath(this.list[i].path);

                if (listPath == path) {
                    return;
                }

                if (this.list[i].priority < item.priority) {
                    this.list.splice(i, 0, item);
                    return;
                }
            }

            this.list.push(item);
        },

        /**
         * 获取桌面路径
         * @returns {*}
         */
        getDesktopPath() {
            return core.correctPath(remote.app.getPath('home') + '/desktop');
        },

        /**
         * 获取开始菜单路径
         * @returns {*}
         */
        getStartMenuPaths() {
            let startMenuPath = '/Microsoft/Windows/Start Menu';

            return [
                core.correctPath(AllUserProfilePath + startMenuPath),
                core.correctPath(AppDataPath + startMenuPath),
                core.correctPath(LocalAppDataPath + startMenuPath),
            ];
        },

        /**
         * 文件或目录是否在桌面上
         * @param path
         * @returns {boolean}
         */
        isInDesktop(path) {
            return this.isInDirectory(path, this.getDesktopPath());
        },

        /**
         * 判断某个路径是否在某个目录下
         * @param child
         * @param parent
         * @returns {boolean}
         */
        isInDirectory(child, parent) {
            parent = core.correctPath(parent + '/');
            child = core.correctPath(child + '/');

            return child.indexOf(parent) !== -1;
        },

        isInStartMenu(file) {
            let startMenuPaths = this.getStartMenuPaths();

            for (let i = 0; i < startMenuPaths.length; i++) {
                if (this.isInDirectory(file, startMenuPaths[i])) {
                    return true;
                }
            }

            return false;
        },

        inCustomDirectiry(file) {
            let dirs = this.settings.directories;

            for (let i = 0; i < dirs.length; i++) {
                let dir = dirs[i];
                if (this.isInDirectory(file, dir.path)) {
                    return true;
                }
            }

            return false;
        },

        /**
         * 添加一些固定的文件, 比如hosts文件
         */
        addBuildInFilesToIndexes() {
            let all = FileType.getAllBuildInFiles();

            //有别名的程序
            for (let path in all) {
                path = core.correctPath(path);

                this.updateIndex(path, {
                    basePriority: this.createPriorityLocation(1),
                    addFileOption: {
                        ignoreExtnames: ['.dll']
                    },

                    arguments: all[path].arguments,
                });
            }
        },

        /**
         * 初始化目录(固定目录+用户配置目录+环境变量目录)
         */
        initDirectories(oldSetting) {
            this.directories = {};

            // 桌面
            let desktopPath = this.getDesktopPath();
            this.directories[desktopPath] = {
                path: this.getDesktopPath(),

                watchingOption: {
                    depth: 2
                },

                addFileOption: {

                }
            };

            //开始菜单
            let startMenuDirs = this.getStartMenuPaths();

            for (let i in startMenuDirs) {
                this.directories[startMenuDirs[i]] = {
                    path: startMenuDirs[i],

                    watchingOption: {
                        depth: -1
                    },

                    addFileOption: {
                        extname: ['.lnk']
                    }
                };
            }

            // 系统目录
            let system32Dir = core.correctPath(SystemRootPath + '/system32');
            this.directories[system32Dir] = {
                path: system32Dir,

                watchingOption: {
                    depth: 0
                },

                addFileOption: {
                    extname: ['.exe', '.cpl']
                }
            };

            //自定义路径
            let directories = this.settings.directories;
            let dirMap = {};

            for (let i = 0; i < directories.length; i++) {
                let dir = directories[i];
                let path = core.correctPath(dir.path);
                dirMap[path] = true;

                if (path == '') {
                    continue;
                }

                let extname = [...new Set(FileType.getExecuteableExtnames().concat(dir.allow_extnames.toLowerCase().split('|').filter(a => !!a)))];
                let ignoreExtnames = [...new Set(dir.ignore_extnames.toLowerCase().split('|').filter(a => !!a))];

                //已经存在
                if (this.directories[path]) {
                    this.directories[path].watchingOption.depth = dir.depth;
                    this.directories[path].addFileOption.extname = extname;
                    this.directories[path].addFileOption.ignoreExtnames = ignoreExtnames;
                    continue;
                }

                let watchOption = {};

                if (dir.depth != -1) {
                    watchOption.depth = dir.depth;
                }

                this.directories[path] = {
                    path,
                    watchingOption: watchOption,
                    addFileOption: {
                        extname: extname,
                        ignoreExtnames: ignoreExtnames
                    }
                };
            }

            if (oldSetting) {
                for (let i = 0; i < oldSetting.directories.length; i++) {
                    let dir = oldSetting.directories[i];
                    let path = core.correctPath(dir.path);

                    if (!dirMap[path]) {
                        this.removeIndexByListernDir(path);
                        delete this.scannedDirectories[path];
                        console.log('移除目录', path);

                        if (this.watchingDirectories[path]) {
                            this.watchingDirectories[path].close();
                            delete this.watchingDirectories[path];

                            if (this.searchIndexes[path]) {
                                delete this.searchIndexes[path];
                            }
                        }
                    }
                }
            }


            // 环境变量
            // let envPaths = [...new Set(process.env.PATH.split(';').map(a => core.correctPath(a.toLowerCase().trim())).filter(a=>!!a))];
            let envPaths = [];

            for (let i = 0; i < envPaths.length; i++) {
                let envpath = core.correctPath(envPaths[i]);

                if (this.directories[envpath]) {
                    continue;
                }

                this.directories[envpath] = {
                    path: envpath,

                    watchingOption: {
                        depth: 0
                    },

                    addFileOption: {
                        extname: ['.exe']
                    }
                };
            }

            for (let path in this.watchingDirectories) {
                if (!this.directories[path]) {
                    this.watchingDirectories[path].close();
                    delete this.watchingDirectories[path];
                }
            }

            setTimeout(() => this.watchDirectory(), 30);
        },

        /**
         * 获取目录
         * @param file
         * @returns {*}
         */
        getDirectoryIndex(file) {
            file = core.correctPath(file.toLowerCase());
            return this.directories[file];
        },

        /**
         * 将文件添加或更新到搜索索引
         * @param file
         * @param options
         * @returns {{icon: {src: string, type: string}, description: string, target: *, running: boolean, path: *, realIsDirectory: boolean, nameIndex: (boolean|*[]|{}|{raw, simple: *[], full: *[]}|{x: number, match: string, y: number, fully: boolean}), targetState: null, descriptionIndex: null, name: string, state: null, tag: *, isShortcut: boolean, extname: string, targetExtname: string}|null}
         */
        updateIndex(file, options) {
            file = core.correctPath(file);
            options = options ? options : {};
            let condition = options.addFileOption ? options.addFileOption : {};
            let extname = condition.extname;
            let excludeExtname = condition.ignoreExtnames;
            let fileext = path.extname(file).toLowerCase();
            let stat = null;

            try {
                stat = fs.statSync(file);
            } catch (e) {
                console.warn(e.message);
                return;
            }

            if (stat.isDirectory()) {
                fileext = 'dir';
            }

            let attr = fswin.getAttributesSync(file);

            if (attr.IS_HIDDEN) {
                return null;
            }

            if (extname && extname.indexOf(fileext) == -1) {
                return null;
            }

            if (excludeExtname && excludeExtname.indexOf(fileext) != -1) {
                return null;
            }

            let indexes = this.createFileIndex(file, options);
            return this.searchIndexes[indexes.id] = indexes;
        },

        /**
         * 删除指定的搜索索引
         * @param file
         */
        removeIndex(file) {
            file = core.correctPath(file.toLowerCase());

            if (this.searchIndexes[file]) {
                delete this.searchIndexes[file];
            }
        },

        /**
         * 删除指定目录的搜索索引
         * @param dir
         */
        removeIndexByListernDir(dir) {
            dir = core.correctPath(dir.toLowerCase());

            for (let i in this.searchIndexes) {
                if (this.searchIndexes[i].listernDir == dir) {
                    delete this.searchIndexes[i];
                }
            }
        },

        /**
         * 拼音搜索引擎
         */
        getPinyinEngine() {
            if (this.pinyinEngine) {
                return this.pinyinEngine;
            }

            return this.pinyinEngine = require(`${srcPath}/mainlib/pinyin`);
        },

        /**
         * 创建文件或目录的索引
         * @param file
         * @param options
         * @returns {{icon: {src: string, type: string}, description: string, target: *, running: boolean, path: *, realIsDirectory: boolean, nameIndex: (boolean|[]|{}|{raw, simple: [], full: []}|{x: number, match: string, y: number, fully: boolean}), targetState: null, descriptionIndex: null, name: string, state: null, tag: *, isShortcut: boolean, extname: string, targetExtname: string}}
         */
        createFileIndex(file, options) {
            options = options ? options : {};
            let extname = path.extname(file).toLowerCase();
            let target = file;
            let result = {};
            let name = path.basename(file);
            let description = '';
            let descriptionIndex = null;
            let secondName = null;

            let state = null;
            let icon = FileType.getExtnameDefaultIcon('');
            icon.isdefault = true;
            let iconFetchFile = null;
            let iconIndex = 0;

            let realIsDirectory = false;
            let targetState = null;
            let targetArguments = null;
            let args = options.arguments;

            let targetExtname = path.extname(target).toLowerCase();
            let isShortcut = false;

            if (fs.existsSync(file)) {
                state = fs.statSync(file);
                realIsDirectory = state.isDirectory();
            }

            let notarget = false;

            if (extname == '.lnk') {
                isShortcut = true;
                result = wh.readShortcutFile(file);
                description = result.description;

                if (result.target) {
                    target = core.parseVarPath(result.target);
                    targetExtname = path.extname(target).toLowerCase();

                    if (fs.existsSync(result.target)) {
                        targetState = fs.statSync(result.target);
                        realIsDirectory = targetState.isDirectory();
                    }

                    targetArguments = result.args;
                } else {
                    notarget = true;
                }

                if (result.icon) {
                    iconFetchFile = core.parseVarPath(result.icon);
                    iconIndex = result.iconIndex;
                }

                name = name.substr(0, name.length - extname.length);

                if (description && description[0] == '@') {
                    description = null;
                }
            }

            let buildInFileInfo = FileType.getBuildInFileInfo(targetArguments ? file : target);

            if (buildInFileInfo && buildInFileInfo.alias) {
                secondName = name;
                name = buildInFileInfo.alias;
            }

            if (description) {
                descriptionIndex = this.getPinyinEngine().translate(description);
            }

            let id = args ? file + ':' + args : file;

            return {
                id,
                name,
                secondName,
                isShortcut,
                state: state,
                secondNameIndex: secondName ? this.getPinyinEngine().translate(secondName) : null,
                nameIndex: this.getPinyinEngine().translate(name),
                path: file,
                extname: extname,
                realIsDirectory,
                arguments: args,

                description,
                descriptionIndex,
                notarget,
                target,
                targetExtname,
                targetArguments,
                targetState: targetState,

                icon,
                iconFetchFile,
                iconIndex,

                tag: this.getTag(file),
                running: false,

                listernDir: options ? options.path : null,
                basePriority: options.basePriority ? options.basePriority : 0,
            }
        },

        /**
         * 开始监听目录及其文件的变更
         */
        watchDirectory() {
            let chokidar = require("chokidar");

            for (let i in this.directories) {
                let dir = this.directories[i];
                let option = {
                    ignoreInitial: true,
                };

                if (this.watchingDirectories[dir.path]) {
                    continue;
                }

                if (!Type.isUndefined(dir.watchingOption)) {
                    option = Object.assign(option, dir.watchingOption);
                }

                let watcher = chokidar.watch(dir.path, option);
                this.watchingDirectories[dir.path] = watcher;

                watcher
                    .on('add', path => this.updateIndex(path, dir))
                    .on('change', path => this.updateIndex(path, dir))
                    .on('unlink', path => this.removeIndex(path))
                    .on('addDir', path => {
                        if (dir.path.toLowerCase() != path.toLowerCase()) {
                            this.updateIndex(path, dir)
                        }
                    })
                    .on('unlinkDir', path => {
                        if (dir.path.toLowerCase() != path.toLowerCase()) {
                            this.removeIndex(path);
                        }
                    });
            }
        },

        /**
         * 打开设置窗口
         */
        openSettingWindow() {
            WndMgr.open('setting');
        },

        /**
         * 获取匹配项的标题提示
         * @param row
         * @returns {string}
         */
        getRowTitle(row) {
            let description = row.description ? "\n\n【详细信息】\n" + row.description : '';
            let args = row.targetArguments ? `\n【参数】${row.targetArguments}` : '';

            if (row.isShortcut) {
                return `快捷方式\n【位置】${row.path}\n【目标】${row.target}${args}${description}`;
            } else {
                return `${row.path}${description}`;
            }
        },

        /**
         * 创建右上角菜单
         */
        createMenu() {
            let self = this;

            //右上角菜单
            this.menu = new Menu();
            this.menu.append(new MenuItem({ label: '设置', click() { self.openSettingWindow() }, icon: srcPath + '/static/img/icon/settings_16.png' }));
            this.menu.append(new MenuItem({ type: 'separator' }));
            this.menu.append(new MenuItem({ label: '关于..', click() { self.openAboutWindow() } }));
            this.menu.append(new MenuItem({ type: 'separator' }));
            this.menu.append(new MenuItem({ label: '退出', click() { remote.app.exit() } }));

        },

        /**
         * 创建匹配项的右键菜单
         * @param item
         * @param popupOptions
         * @returns {Electron.Menu}
         */
        createRightMenu(item, popupOptions) {
            let self = this;
            if (this.itemRightMenu) {
                delete this.itemRightMenu;
            }

            let menuOptions = [{
                    label: '打开',
                    click() {
                        self.shellOpenItem(item)
                    }
                },

                {
                    label: '打开所在目录',
                    click() {
                        self.shellExplorerSelect(item.path);
                    }
                },

                {
                    label: '打开目标所在目录',
                    visible: item.isShortcut && !item.notarget,
                    click() {
                        self.shellExplorerSelect(item.target);
                    }
                },

                {
                    type: 'separator'
                },

                {
                    label: '复制名称',
                    click() {
                        clipboard.writeText(path.basename(item.path));
                    }
                },

                {
                    label: '复制路径',
                    click() {
                        clipboard.writeText(item.path);
                    }
                },


                {
                    label: '复制所在目录路径',
                    click() {
                        clipboard.writeText(path.dirname(item.path));
                    }
                },

                {
                    label: '复制目标路径',
                    visible: item.isShortcut && !item.notarget,
                    click() {
                        clipboard.writeText(item.target);
                    }
                },

                {
                    label: '复制目标所在目录路径',
                    visible: item.isShortcut && !item.notarget,
                    click() {
                        clipboard.writeText(path.dirname(item.target));
                    }
                },
            ];

            let allowMenuOption = {};
            let extraMenuOptions = [];
            // let menu = Menu.buildFromTemplate(menuOptions);
            let menuVisiable = true;

            let createMenu = (filetype) => {
                let realOption = Type.clone(menuOptions);

                if (!Type.isUndefined(filetype)) {
                    let regMenuOption = RegRightMenuCache[filetype];

                    if (regMenuOption) {
                        realOption = realOption.concat(regMenuOption);
                    }

                }

                return Menu.buildFromTemplate(realOption);
            };

            let convertCmd = str => {
                str = str.replace(/(%V)|(%L)|(%0)|(%1)|(%2)/g, item.path).trim();
                return str;
            };

            let target = item.target;
            item.realIsDirectory = fs.statSync(target).isDirectory();

            if (item.realIsDirectory) {
                let menu = createMenu('dir');
                menu.popup({
                    ...popupOptions,
                    callback() {
                        menuVisiable = false;
                    }
                });

                this.forceCursor = "progress";

                let resultCallback = (regMenuOptions) => {
                    this.forceCursor = false;
                    let menucache = RegRightMenuCache['dir'];

                    if (regMenuOptions) {
                        RegRightMenuCache['dir'] = regMenuOptions;

                        if (JSON.stringify(regMenuOptions) == JSON.stringify(menucache)) {
                            return;
                        }
                    }

                    if (menuVisiable) {
                        menu.closePopup();
                        menu = createMenu('dir');
                        menu.popup(popupOptions);
                    }
                };

                let key = "HKCR\\Directory\\shell";
                let directoryMenuOption = [];

                regeditTools.list(key, (e, data) => {
                    if (e) {
                        console.error(e);
                        resultCallback();
                        return;
                    }

                    let keys = data[key].keys;
                    let listKeys = keys.map(a => `${key}\\${a}`);

                    if (!listKeys.length) {
                        resultCallback();
                        return;
                    }

                    regeditTools.list(listKeys, (ae, adata) => {
                        if (ae) {
                            console.error(ae);
                            resultCallback();
                            return;
                        }

                        let menuOptionKeys = [];

                        for (let i = 0; i < listKeys.length; i++) {
                            let subkey = listKeys[i];
                            let name = adata[subkey].values[''] ? adata[subkey].values[''].value : null;
                            let iconPath = adata[subkey].values['Icon'] ? adata[subkey].values['Icon'].value : null;

                            if (!name || name[0] == '@') {
                                continue;
                            }

                            if (adata[subkey].keys.indexOf('command') == -1) {
                                continue;
                            }

                            let op = { name };

                            if (iconPath) {
                                iconPath = iconPath.replace(/,.*?$/, '');

                                let extname = path.extname(iconPath);

                                if (extname == 'dll') {
                                    iconPath = null;
                                }
                            }

                            let menuOptionKey = subkey + '\\command';
                            menuOptionKeys.push(menuOptionKey);

                            if (iconPath) {
                                op.icon = iconPath;
                            }

                            allowMenuOption[menuOptionKey] = op;
                        }

                        if (!menuOptionKeys.length) {
                            resultCallback();
                            return;
                        }

                        regeditTools.list(menuOptionKeys, (be, bdata) => {
                            if (be) {
                                console.error(be);
                                resultCallback();
                                return;
                            }

                            for (let i = 0; i < menuOptionKeys.length; i++) {
                                let k = menuOptionKeys[i];
                                let cmd = bdata[k].values[''] ? bdata[k].values[''].value : null;

                                if (!cmd) {
                                    continue;
                                }

                                directoryMenuOption.push({
                                    label: allowMenuOption[k].name,
                                    icon: allowMenuOption[k].icon,
                                    click: () => {
                                        let cmdStr = convertCmd(cmd);
                                        cmdStr = cmdStr.replace(/"/g, '""');
                                        let vbsName = 'jkqs_rightmenu_run_' + new Date().getTime() + '.vbs';
                                        let vbsPath = process.env.TEMP + `/${vbsName}`;

                                        let vbs = `Dim oShell
Set oShell = WScript.CreateObject ("WSCript.shell")
oShell.run "${cmdStr}"
Wscript.Sleep 100
Set objFSO = CreateObject("Scripting.FileSystemObject")
strScript = Wscript.ScriptFullName
objFSO.DeleteFile(strScript)`;

                                        fs.writeFileSync(vbsPath, vbs);
                                        childProcess.exec(vbsPath);
                                    }
                                });
                            }

                            if (directoryMenuOption.length > 0) {
                                extraMenuOptions.push({ type: 'separator' });

                                for (let i = 0; i < directoryMenuOption.length; i++) {
                                    extraMenuOptions.push(directoryMenuOption[i]);
                                }
                            }

                            let tmpOptions = [].concat(extraMenuOptions);

                            let _getnexticon = () => {
                                if (tmpOptions.length == 0) {
                                    resultCallback(extraMenuOptions);
                                    return;
                                }

                                let option = tmpOptions.pop();

                                if (option.icon) {
                                    let extname = path.extname(option.icon);

                                    if (extname == '.exe') {
                                        remote.app.getFileIcon(option.icon, { size: 'small' }).then(img => {
                                            if (img.isEmpty()) {
                                                delete option.icon;
                                            }

                                            option.icon = img;
                                            _getnexticon();
                                        });
                                    } else if (FileType.getExtnameType(extname) != 'img') {
                                        delete option.icon;
                                    } else {
                                        _getnexticon();
                                    }
                                } else {
                                    _getnexticon();
                                }
                            };

                            _getnexticon();
                        });
                    });
                });
            } else {
                createMenu().popup()
            }
        },

        /**
         * 显示匹配项的右键菜单
         * @param activedIndex
         */
        showRightMenu(activedIndex, e) {
            if (activedIndex) {
                this.activedIndex = activedIndex;
            }

            let item = this.list[activedIndex];
            this.createRightMenu(item, { x: e.clientX, y: e.clientY });

        },

        /**
         * 执行windows操作, 打开资源管理器并选中指定的文件或目录
         * @param path
         */
        shellExplorerSelect(path) {
            childProcess.exec(`explorer.exe /select, "${path}"`);
        },

        /**
         * 执行windows操作, 打开一个匹配项
         * @param item
         */
        shellOpenItem(item) {
            if (item.path) {
                if (item.isShortcut && !fs.existsSync(item.target)) {
                    remote.dialog.showMessageBox(this.mainWindow, {
                        type: 'error',
                        title: '错误',
                        message: `该项目的目标 "${item.target}" 已经不存在\n\n【快捷方式文件位置】${item.path}`
                    });
                    return;
                }

                this.mainWindow.animateHide();

                if (!item.isShortcut && item.arguments) {
                    childProcess.exec(item.path + ' ' + item.arguments);
                } else {
                    remote.shell.openItem(item.path);
                }
            }
        },

        /**
         * 执行windows操作, 打开/运行一个文件或目录
         * @param path
         */
        shellOpenPath(path) {
            remote.shell.openItem(path);
        },

        openAboutWindow() {
            WndMgr.open('about');
        },
    }
});